﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using GIMS.Def;
using GIMS.Def.Arguments;
using GIMS.Def.Operators;
using GIMS.GUI.GraphItems;
using System.ComponentModel;
using GIMS.Exec;
using System.Collections.ObjectModel;
using GIMS.Def.Flags;
using GIMS.GUI.GraphManagement;
using GIMS.Def.Settings;
using System.IO;

namespace GIMS.GUI
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private CustomVertex currentVertexForPreview;
        private ObservableCollection<OperatorRepresenter> operators;
        private IEnumerable<ProgramRepresenter> programs;
        private Program currentProgram;

        #region Constructors

        public MainWindowViewModel()
        {
            operators = new ObservableCollection<OperatorRepresenter>();
            Graph = new CustomGraph(false);
            LayoutAlgorithmType = "EfficientSugiyama";
            Graph.refreshNeededEvent += new CustomGraph.RefreshNeededEventHandler(Graph_refreshNeededEvent);
            Graph.VertexAdded += new QuickGraph.VertexAction<CustomVertex>(Graph_VertexAdded);
            var globalSettings = GlobalSettings.Instance;
            globalSettings.PropertyChanged += new PropertyChangedEventHandler(globalSettings_PropertyChanged);
        }

        #endregion Constructors

        #region Public Methods

        public void IncrementEdgeSequence(CustomEdge edge)
        {
            var maxSequence = Graph.Edges.Count(p => p.Target == edge.Target);
            if (edge.Sequence < maxSequence)
            {
                var newSequence = edge.Sequence + 1;
                //if an edge with newSequence exists, decrement its sequence
                var oldEdgeWithNewSequence = Graph.Edges.First(p => p.Target == edge.Target && p.Sequence == newSequence);
                if (null != oldEdgeWithNewSequence)
                {
                    oldEdgeWithNewSequence.Sequence--;
                }
                edge.Sequence = newSequence;
                RefreshPreviewImages(edge.Target);
            }
        }

        public void DecrementEdgeSequence(CustomEdge edge)
        {
            if (edge.Sequence > 1)
            {
                var newSequence = edge.Sequence - 1;
                //if an edge with newSequence exists,  increment its sequence
                var oldEdgeWithNewSequence = Graph.Edges.First(p => p.Target == edge.Target && p.Sequence == newSequence);
                if (null != oldEdgeWithNewSequence)
                {
                    oldEdgeWithNewSequence.Sequence++;
                }
                edge.Sequence = newSequence;
                RefreshPreviewImages(edge.Target);
            }
        }

        public string CreateScript()
        {
            var skriptCreator = new ScriptCreator(this);
            bool isExecutable;
            return skriptCreator.GetScript(Graph.GetLast(), out isExecutable);
        }

        public bool IsScriptExecutable()
        {
            if (!Graph.Vertices.Any())
                return false;
            if (currentProgram == Program.convert && !Graph.HasSave)
                return false;

            var skriptCreator = new ScriptCreator(this);
            bool isExecutable;
            skriptCreator.GetScript(Graph.GetLast(), out isExecutable);
            return isExecutable;
        }

        public ScripStatusEnum GetScriptStatus()
        {
            int saveParam = 0;
            int loadParam = 0;
            PathArgument pathArgument;

            if (!Graph.Vertices.Any())
                return ScripStatusEnum.NoParam;

            foreach(CustomVertex vertex in Graph.Vertices)
            {
                if(vertex.IMOperator.Argument!=null && vertex.IMOperator.Argument.Type==ArgumentType.path && ((PathArgument)vertex.IMOperator.Argument).IsParameter)
                {
                    if(vertex.IMOperator.Name=="Load")
                    {
                        loadParam++;
                    }
                    else
                    {
                        saveParam++;
                    }
                }
            }
            if(saveParam==0 && loadParam==0)
                return ScripStatusEnum.NoParam;

            if(saveParam==1 && loadParam==1)
                return ScripStatusEnum.CorrectParam;

            return ScripStatusEnum.IncorrectParam;
        }

        public void ExecuteGraphScript()
        {
            IExecutor executor = new Executor();
            var currentProgramName = AvailablePrograms.Single(p => p.Program == CurrentProgram).ProgramName;
            executor.ExecuteCommand(currentProgramName, CreateScript());
        }

        public bool IsIMPathCorrect
        {
            get
            {
                bool isCorrect = false;
                var path = ConfigurationManager.AppSettings["IMPath"];
                if (!string.IsNullOrEmpty(path)
                    && Directory.Exists(path)
                    && File.Exists(Path.Combine(path, "convert.exe")))
                {
                    isCorrect = true;
                }

                return isCorrect;
            }
        }

        #endregion Public Methods

        #region Public Properties

        public CustomGraph Graph
        {
            get;
            private set;
        }

        public string LayoutAlgorithmType
        {
            get;
            private set;
        }

        public IEnumerable<ProgramRepresenter> AvailablePrograms
        {
            get
            {
                if (null == programs)
                {
                    var programsList = new List<ProgramRepresenter>();
                    foreach (int i in Enum.GetValues(typeof(Program)))
                    {
                        programsList.Add(new ProgramRepresenter((Program)i));
                    }
                    programs = programsList;
                }
                return programs;
            }
        }

        public Program CurrentProgram
        {
            get { return currentProgram; }
            set
            {
                currentProgram = value;
                CreateOperatorRepresenterList();
                OnPropertyChanged("CurrentProgramName");
            }
        }

        public string CurrentProgramName
        {
            get { return 0 == currentProgram ? "" : Enum.GetName(typeof(Program), CurrentProgram); }
        }

        public ObservableCollection<OperatorRepresenter> Operators
        {
            get
            {
                if (null == operators)
                {
                    CreateOperatorRepresenterList();
                }
                return operators;
            }
        }

        public CustomVertex CurrentVertexForPreview
        {
            get { return currentVertexForPreview; }
            set
            {
                currentVertexForPreview = value;
                OnPropertyChanged("CurrentVertexForPreview");
            }
        }

        #endregion Public Properties

        #region Private Helper Methods

        private void CreateOperatorRepresenterList()
        {
            operators.Clear();
            var availableOperators = OperatorFactory.AvailableOperators.Where(p => (p.PossiblePrograms & CurrentProgram) == CurrentProgram);
            foreach (var imOperator in availableOperators)
            {
                operators.Add(new OperatorRepresenter(imOperator));
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void Graph_refreshNeededEvent(object sender, RefreshNeededEventArgs e)
        {
            RefreshPreviewImages(e.Vertex);
        }

        private void globalSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Settings")
            {
                var startVertices = Graph.Vertices.Where(p => Graph.GetParentsCount(p) == 0);
                foreach (var vertex in startVertices)
                {
                    RefreshPreviewImages(vertex);
                }
            }
        }

        private void Graph_VertexAdded(CustomVertex vertex)
        {
            CurrentVertexForPreview = vertex;
        }

        public void RefreshPreviewImages(CustomVertex vertex)
        {
            IExecutor executor = new Executor();
            var skriptCreator = new ScriptCreator(this);

            bool isExecutable;
            string command = skriptCreator.GetScriptPreviwImageScript(vertex, out isExecutable);
            if (!isExecutable)
                return;

            try
            {
                if (currentProgram == Program.mogrify)
                {
                    vertex.PreviewImagePath = executor.CreatePreview("convert", command);
                }
                else if (vertex.IMOperator is SaveOperator)
                {
                    vertex.PreviewImagePath = Graph.GetParents(vertex).Single().PreviewImagePath;
                }
                else
                {
                    var currentProgramName = AvailablePrograms.Single(p => p.Program == CurrentProgram).ProgramName;
                    vertex.PreviewImagePath = executor.CreatePreview(currentProgramName, command);
                }
            }
            catch (IMFileNotFoundException ex)
            {
                vertex.PreviewImagePath = String.Empty;
            }
        }

        #endregion Private Helper Methods
    }
}
